// Copyright 2021 The Lynx Authors. All rights reserved.
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.

#include "core/renderer/utils/lynx_env.h"

#include <algorithm>
#include <cstdbool>
#include <unordered_map>
#include <utility>

#include "base/trace/native/trace_event.h"
#include "core/base/trace/trace_event_def.h"
#include "core/renderer/trace/renderer_trace_event_def.h"
#include "core/renderer/utils/lynx_trail_hub.h"
#include "third_party/rapidjson/stringbuffer.h"
#include "third_party/rapidjson/writer.h"

#if OS_ANDROID
#include "core/renderer/utils/android/lynx_env_android.h"
#endif

namespace lynx {
namespace tasm {

LynxEnv& LynxEnv::GetInstance() {
  static base::NoDestructor<LynxEnv> instance;
  return *instance;
}

void LynxEnv::onPiperInvoked(const std::string& module_name,
                             const std::string& method_name,
                             const std::string& param_str,
                             const std::string& url,
                             const std::string& invoke_session) {
#if OS_ANDROID
  tasm::LynxEnvAndroid::onPiperInvoked(module_name, method_name, param_str,
                                       url);
#endif
}

void LynxEnv::onPiperResponsed(const std::string& module_name,
                               const std::string& method_name,
                               const std::string& url,
                               const std::string& response,
                               const std::string& invoke_session) {
#if OS_ANDROID
#endif
}

bool LynxEnv::ContainKey(const std::string& key) {
  std::lock_guard<std::mutex> lock(mutex_);
  return local_env_map_.count(key) > 0;
}

void LynxEnv::SetBoolLocalEnv(const std::string& key, bool value) {
  SetLocalEnv(key, value ? kLocalEnvValueTrue : kLocalEnvValueFalse);
}

void LynxEnv::SetLocalEnv(const std::string& key, const std::string& value) {
  std::lock_guard<std::mutex> lock(mutex_);
  auto old_value = local_env_map_.find(key);
  if (old_value != local_env_map_.end()) {
    local_env_map_.erase(old_value);
  }
  local_env_map_.emplace(key, value);
}

void LynxEnv::SetGroupedEnv(const std::string& key, bool value,
                            const std::string& group_key) {
  std::lock_guard<std::mutex> lock(mutex_);
  auto it = env_group_sets_.find(group_key);
  if (it == env_group_sets_.end()) {
    std::unordered_set<std::string> new_set;
    it = env_group_sets_.insert(it, std::make_pair(group_key, new_set));
  }
  if (value) {
    it->second.insert(key);
  } else {
    it->second.erase(key);
  }
}

void LynxEnv::SetGroupedEnv(
    const std::unordered_set<std::string>& new_group_values,
    const std::string& group_key) {
  std::lock_guard<std::mutex> lock(mutex_);
  auto old_value = env_group_sets_.find(group_key);
  if (old_value != env_group_sets_.end()) {
    env_group_sets_.erase(old_value);
  }
  env_group_sets_.emplace(group_key, new_group_values);
}

long LynxEnv::GetLongEnv(Key key, int default_value, EnvType type) {
  TRACE_EVENT(LYNX_TRACE_CATEGORY, LYNX_ENV_GET_LONG_ENV, "key",
              static_cast<uint64_t>(key));
  std::optional<std::string> string_result = GetStringEnv(key, type);
  if (!string_result.has_value() || (*string_result).empty()) {
    return default_value;
  }
  char* end_tag;
  long result = std::strtol((*string_result).c_str(), &end_tag, 10);
  if (*end_tag != '\0') {
    return default_value;
  }
  return result;
}

bool LynxEnv::ConvertToBool(const std::string& value) {
  if (value == kLocalEnvValueTrue) {
    return true;
  } else if (value == kLocalEnvValueFalse) {
    return false;
  } else {
    static const std::string true_result = "true";
    return value.size() == true_result.size() &&
           std::equal(value.begin(), value.end(), true_result.begin(),
                      [](char a, char b) {
                        return std::tolower(a) == std::tolower(b);
                      });
  }
}

bool LynxEnv::GetBoolEnv(const std::string& key, bool default_value) {
  TRACE_EVENT(LYNX_TRACE_CATEGORY, LYNX_ENV_GET_BOOL_ENV, "key", key);
  std::lock_guard<std::mutex> lock(mutex_);
  auto it = local_env_map_.find(key);
  if (it != local_env_map_.end()) {
    return ConvertToBool(it->second);
  }
  return default_value;
}

bool LynxEnv::GetBoolEnv(Key key, bool default_value, EnvType type) {
  TRACE_EVENT(LYNX_TRACE_CATEGORY, LYNX_ENV_GET_BOOL_ENV, "key",
              static_cast<uint64_t>(key));
  std::optional<std::string> string_result = GetStringEnv(key, type);
  if (!string_result.has_value() || (*string_result).empty()) {
    return default_value;
  }
  return ConvertToBool(*string_result);
}

std::optional<std::string> LynxEnv::GetStringEnv(Key key, EnvType type) {
  TRACE_EVENT(LYNX_TRACE_CATEGORY, LYNX_ENV_GET_STRING_ENV, "key",
              static_cast<uint64_t>(key));
  std::optional<std::string> result = std::nullopt;
  switch (type) {
    case EnvType::EXTERNAL: {
      result = GetExternalEnv(key);
    } break;
    case EnvType::LOCAL: {
      result = GetLocalEnv(key);
    } break;
    default: {
      DCHECK(false);
    } break;
  }
  return result;
}

std::string LynxEnv::GetDebugDescription() {
  rapidjson::StringBuffer buffer;
  rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
  writer.StartObject();
  for (Key key = (Key)0; key < Key::END_MARK;) {
    std::string key_string = GetEnvKeyString(key);
    std::optional<std::string> value = GetStringEnv(key);
    if (value.has_value()) {
      writer.Key(key_string.c_str());
      writer.String((*value).c_str());
    }
    key = (Key)((uint64_t)key + 1);
  }
  writer.EndObject();
  std::string result = buffer.GetString();
  return result;
}

void LynxEnv::SetEnvMask(const std::string& key, bool value) {
  std::lock_guard<std::mutex> lock(mutex_);
  auto old_value = env_mask_map_.find(key);
  if (old_value != env_mask_map_.end()) {
    env_mask_map_.erase(old_value);
  }
  env_mask_map_.emplace(key, value);
}

bool LynxEnv::GetEnvMask(Key key) {
  std::string key_string = GetEnvKeyString(key);
  auto value = env_mask_map_.find(key_string);
  return value != env_mask_map_.end() ? (*value).second : true;
}

std::unordered_set<std::string> LynxEnv::GetGroupedEnv(
    const std::string& group_key) {
  std::lock_guard<std::mutex> lock(mutex_);
  auto it = env_group_sets_.find(group_key);
  if (it != env_group_sets_.end()) {
    return it->second;
  }
  return std::unordered_set<std::string>();
}

bool LynxEnv::IsDevToolComponentAttach() {
  return GetBoolEnv(Key::DEVTOOL_COMPONENT_ATTACH, false, EnvType::LOCAL);
}

bool LynxEnv::IsLynxDebugEnabled() {
  return GetBoolEnv(Key::LYNX_DEBUG_ENABLED, false, EnvType::LOCAL);
}

bool LynxEnv::IsDevToolEnabled() {
  return GetBoolEnv(Key::ENABLE_DEVTOOL, false, EnvType::LOCAL);
}

bool LynxEnv::IsLogBoxEnabled() {
  return IsLynxDebugEnabled() &&
         GetBoolEnv(Key::ENABLE_LOGBOX, true, EnvType::LOCAL);
}

bool LynxEnv::IsQuickjsCacheEnabled() {
  bool enable_quickjs_cache = GetBoolEnv(
      Key::ANDROID_DISABLE_QUICKJS_CODE_CACHE, true, EnvType::EXTERNAL);
  if (!enable_quickjs_cache) {
    return false;
  }
  return GetBoolEnv(Key::ENABLE_QUICKJS_CACHE, true, EnvType::LOCAL);
}

bool LynxEnv::IsDisableTracingGC() {
  return GetBoolEnv(Key::DISABLE_TRACING_GC, false);
}

bool LynxEnv::IsLayoutPerformanceEnabled() {
  return GetBoolEnv(Key::LAYOUT_PERFORMANCE_ENABLE, false, EnvType::LOCAL);
}

bool LynxEnv::IsPiperMonitorEnabled() {
  return GetBoolEnv(Key::ENABLE_PIPER_MONITOR, false, EnvType::LOCAL);
}

bool LynxEnv::IsDomTreeEnabled(bool debuggable) {
  return (IsDevToolEnabled() || debuggable) &&
         GetBoolEnv(Key::ENABLE_DOM_TREE, true, EnvType::LOCAL);
}

bool LynxEnv::GetVsyncAlignedFlushGlobalSwitch() {
  return GetBoolEnv(Key::ENABLE_VSYNC_ALIGNED_FLUSH_LOCAL, true,
                    EnvType::LOCAL);
}

bool LynxEnv::EnableGlobalFeatureSwitchStatistic() {
  return GetBoolEnv(Key::ENABLE_GLOBAL_FEATURE_SWITCH_STATISTIC, false);
}

bool LynxEnv::EnableFeatureCounter() {
  return GetBoolEnv(Key::ENABLE_FEATURE_COUNTER, false);
}

bool LynxEnv::EnablePresetThreadPriority() {
  return GetBoolEnv(Key::ENABLE_PRESET_THREAD_PRIORITY, false);
}

bool LynxEnv::EnableJSBTiming() {
  return GetBoolEnv(Key::ENABLE_JSB_TIMING, false);
}

bool LynxEnv::EnableAsyncJSBTiming() {
  return GetBoolEnv(Key::ENABLE_ASYNC_JSB_TIMING, false);
}

bool LynxEnv::EnableLongTaskTiming() {
  return GetBoolEnv(Key::ENABLE_LONG_TASK_TIMING, false);
}

bool LynxEnv::EnableMemoryMonitor() {
  return GetBoolEnv(Key::ENABLE_MEMORY_MONITOR, false);
}

bool LynxEnv::EnableJSBlockingMonitor() {
  return GetBoolEnv(Key::ENABLE_JS_BLOCKING_MONITOR, false);
}

uint32_t LynxEnv::GetJSBlockingThresholdMs() {
  return static_cast<uint32_t>(GetLongEnv(Key::JS_BLOCKING_THRESHOLD_MS, 20));
}

uint32_t LynxEnv::GetJSBlockingReportIntervalMs() {
  return static_cast<uint32_t>(
      GetLongEnv(Key::JS_BLOCKING_REPORT_INTERVAL_MS, 5000));
}

uint32_t LynxEnv::TimingMapExceededSize() {
  return static_cast<uint32_t>(GetLongEnv(Key::TIMING_MAP_EXCEEDED_SIZE, 1000));
}

uint32_t LynxEnv::GetMemoryChangeThresholdMb() {
  return static_cast<uint32_t>(GetLongEnv(Key::MEMORY_CHANGE_THRESHOLD_MB, 0));
}

uint32_t LynxEnv::GetMemoryAcquisitionDelaySec() {
  return static_cast<uint32_t>(
      GetLongEnv(Key::MEMORY_ACQUISITION_DELAY_SEC, 2));
}

uint32_t LynxEnv::GetMemoryReportIntervalSec() {
  return static_cast<uint32_t>(
      GetLongEnv(Key::MEMORY_REPORT_INTERVAL_SEC, 20 * 60));
}

bool LynxEnv::IsDevToolConnected() {
  return GetBoolEnv(Key::DEVTOOL_CONNECTED, false, EnvType::LOCAL);
}

bool LynxEnv::EnableHarmonyNewOverlay() {
  return GetBoolEnv(Key::ENABLE_HARMONY_NEW_OVERLAY, false);
}

bool LynxEnv::IsTableDeepCheckEnabled() {
  return GetBoolEnv(Key::ENABLE_TABLE_DEEP_CHECK, false, EnvType::LOCAL);
}

bool LynxEnv::IsDisabledLepusngOptimize() {
  return GetBoolEnv(Key::DISABLE_LEPUSNG_OPTIMIZE, false);
}

std::unordered_set<std::string> LynxEnv::GetActivatedCDPDomains() {
  return GetGroupedEnv("activated_cdp_domains");
}

bool LynxEnv::IsDebugModeEnabled() {
#if ENABLE_TRACE_PERFETTO || ENABLE_TRACE_SYSTRACE || ENABLE_TESTBENCH_RECORDER
  return true;
#else
  return false;
#endif
}

std::optional<std::string> LynxEnv::GetLocalEnv(Key key) {
  std::string key_string = GetEnvKeyString(key);
  std::lock_guard<std::mutex> lock(mutex_);
  if (local_env_map_.count(key_string) > 0) {
    bool mask = GetEnvMask(key);
    return mask ? local_env_map_[key_string] : kLocalEnvValueFalse;
  }
  return std::nullopt;
}

int64_t LynxEnv::GetV8HeapSize() {
  return GetLongEnv(Key::V8_HEAP_SIZE, 0, EnvType::EXTERNAL);
}

std::optional<std::string> LynxEnv::GetExternalEnv(Key key) {
  TRACE_EVENT(LYNX_TRACE_CATEGORY, LYNX_ENV_GET_EXTERNAL_ENV, "key",
              static_cast<uint64_t>(key));
  {
    std::lock_guard lock(external_env_mutex_);
    auto env_it = external_env_map_.find(key);
    if (env_it != external_env_map_.end()) {
      return env_it->second;
    }
  }

  const std::string& key_string = GetEnvKeyString(key);
  std::optional<std::string> string_value =
      LynxTrailHub::GetInstance().GetStringForTrailKey(key_string);
  if (string_value.has_value()) {
    std::lock_guard lock(external_env_mutex_);
    external_env_map_.emplace(key, *string_value);
  }
  return string_value;
}

void LynxEnv::CleanExternalCache() {
  std::lock_guard<std::recursive_mutex> lock(external_env_mutex_);
  external_env_map_.clear();
}

int32_t LynxEnv::GetGlobalQuickContextPoolSize(int32_t default_value) {
  return static_cast<int32_t>(GetLongEnv(Key::GLOBAL_QUICK_CONTEXT_POOL_SIZE,
                                         default_value, EnvType::EXTERNAL));
}

bool LynxEnv::EnableUIOpBatch() {
  return GetBoolEnv(Key::ENABLE_UI_OP_BATCH, false);
}

bool LynxEnv::EnableCSSLazyImport() {
  static bool cached_enable_lazy_import_css_result =
      GetBoolEnv(Key::ENABLE_LAZY_IMPORT_CSS, false);
  return cached_enable_lazy_import_css_result;
}

bool LynxEnv::EnableNewAnimatorFiber() {
  return GetBoolEnv(Key::ENABLE_NEW_ANIMATOR_FIBER, true);
}

bool LynxEnv::IsVSyncTriggeredInUiThreadAndroid() {
  return GetBoolEnv(Key::VSYNC_TRIGGERED_FROM_UI_THREAD_ANDROID, false);
}

bool LynxEnv::IsVSyncPostTaskByEmergency() {
  return GetBoolEnv(Key::VSYNC_POST_TASK_BY_EMERGENCY, false);
}

bool LynxEnv::EnableUseMapBufferForUIProps() {
  return GetBoolEnv(Key::ENABLE_USE_MAP_BUFFER_FOR_UI_PROPS, false);
}

bool LynxEnv::EnablePostDataBeforeUpdateTemplate() {
  return GetBoolEnv(Key::POST_DATA_BEFORE_UPDATE, true);
}

bool LynxEnv::EnableReportListItemLifeStatistic() {
  return GetBoolEnv(Key::ENABLE_REPORT_LIST_ITEM_LIFE_STATISTIC, false);
}

bool LynxEnv::EnableNativeListNested() {
  return GetBoolEnv(Key::ENABLE_NATIVE_LIST_NESTED, true);
}

int32_t LynxEnv::EnableAsyncDestroyEngine() {
  return static_cast<int32_t>(GetLongEnv(Key::ASYNC_DESTROY_ENGINE_COUNT, 0));
}

bool LynxEnv::EnableComponentAsyncDecode() {
  return GetBoolEnv(Key::ENABLE_COMPONENT_ASYNC_DECODE, false);
}

bool LynxEnv::EnableUseContextPool() {
  // TODO(zhoupeng.z): remove this trail option on SDK version 3.2
  return GetBoolEnv(Key::ENABLE_USE_CONTEXT_POOL, true);
}

bool LynxEnv::EnableNativeCreateViewAsync() {
  return GetBoolEnv(Key::ENABLE_NATIVE_CREATE_VIEW_ASYNC, false);
}

bool LynxEnv::EnableSignalAPI() {
  return GetBoolEnv(Key::ENABLE_SIGNAL_API, false);
}

bool LynxEnv::EnableFixedNew() {
  return GetBoolEnv(Key::ENABLE_FIXED_NEW, false);
}

bool LynxEnv::EnableMultiTouch() {
  return GetBoolEnv(Key::ENABLE_MULTI_TOUCH, false);
}

bool LynxEnv::EnableNewIntersectionObserver() {
  return GetBoolEnv(Key::ENABLE_NEW_INTERSECTION_OBSERVER, false);
}

bool LynxEnv::EnableAnimationVsyncOnUIThread() {
  static bool enable_animation_vsync_on_ui_thread =
      GetBoolEnv(Key::ENABLE_ANIMATION_VSYNC_ON_UI_THREAD, false);
  return enable_animation_vsync_on_ui_thread;
}

bool LynxEnv::EnableAnimationInfoReport() {
  static bool enable_animation_info_report =
      GetBoolEnv(Key::ENABLE_ANIMATION_INFO_REPORT, true);
  return enable_animation_info_report;
}

bool LynxEnv::EnableBatchLayoutTaskWithSyncLayout() {
  return GetBoolEnv(Key::ENABLE_BATCH_LAYOUT_TASK_WITH_SYNC_LAYOUT, false);
}

bool LynxEnv::FixParallelZIndexCrash() {
  return GetBoolEnv(Key::FIX_PARALLEL_Z_INDEX_CRASH, true);
}

bool LynxEnv::FixInsertBeforeFixedBug() {
  return GetBoolEnv(Key::FIX_INSERT_BEFORE_FIXED_BUG, true);
}

bool LynxEnv::EnableJSVMRuntime() {
  return GetBoolEnv(Key::ENABLE_JSVM_RUNTIME, false) ||
         GetBoolEnv(Key::ENABLE_JSVM_RUNTIME, false, EnvType::LOCAL);
}

bool LynxEnv::EnableUnifiedPixelPipeline() {
  return GetBoolEnv(Key::ENABLE_UNIFIED_PIXEL_PIPELINE, false);
}

bool LynxEnv::EnableEventHandleRefactor() {
  return GetBoolEnv(Key::ENABLE_EVENT_HANDLE_REFACTOR, false);
}

bool LynxEnv::EnableDecoupledList() {
  return GetBoolEnv(Key::ENABLE_DECOUPLED_LIST, false);
}

bool LynxEnv::EnableReportMTSContextEvent() {
  return GetBoolEnv(Key::ENABLE_REPORT_BTS_CONTEXT_EVENT, false);
}

bool LynxEnv::EnableFiberElementMemoryReport() {
  return GetBoolEnv(Key::ENABLE_FIBER_ELEMENT_MEMORY_REPORT, true);
}

bool LynxEnv::FixFontSizeOverrideDirectionChangeBug() {
  return GetBoolEnv(Key::FIX_FONT_SIZE_OVERRIDE_DIRECTION_CHANGE_BUG, true);
}

bool LynxEnv::EnableNewAnimatorOnPatchFinishOpt() {
  static bool enable_new_animator_on_patch_finish_opt =
      GetBoolEnv(Key::ENABLE_NEW_ANIMATOR_ON_PATCH_FINISH_OPT, true);
  return enable_new_animator_on_patch_finish_opt;
}

bool LynxEnv::FixRadonTransitionPropertyRemoveBug() {
  static bool fix_radon_transition_property_remove_bug =
      GetBoolEnv(Key::FIX_RADON_TRANSITION_PROPERTY_REMOVE_BUG, true);
  return fix_radon_transition_property_remove_bug;
}

bool LynxEnv::EnableGlobalFontCollection() {
  static bool enable_global_font_collection =
      GetBoolEnv(Key::ENABLE_GLOBAL_FONT_COLLECTION, true);
  return enable_global_font_collection;
}

uint32_t LynxEnv::EnableGCOnceOnIdle() {
  static uint32_t cached_enable_gc_once_on_idle =
      static_cast<uint32_t>(GetLongEnv(Key::ENABLE_GC_ONCE_ON_IDLE, 0));
  return cached_enable_gc_once_on_idle;
}

bool LynxEnv::EnableCSSInlineVariables() {
  static bool enable_css_inline_variables =
      GetBoolEnv(Key::ENABLE_CSS_INLINE_VARIABLES, false);
  return enable_css_inline_variables;
}

bool LynxEnv::EnableOptimizeHasOpacity() {
  static bool enable_optimize_has_opacity =
      GetBoolEnv(Key::ENABLE_OPTIMIZE_HAS_OPACITY, true);
  return enable_optimize_has_opacity;
}

bool LynxEnv::DisableJSModeStrip() {
  static bool disable_js_mode_strip =
      GetBoolEnv(Key::DISABLE_JS_MODE_STRIP, false);
  return disable_js_mode_strip;
}

bool LynxEnv::EnableQuickJsThreadChecker() {
  static bool enable_quickjs_thread_checker =
      GetBoolEnv(Key::ENABLE_QUICKJS_THREAD_CHECKER, false);
  return enable_quickjs_thread_checker || IsDevToolEnabled();
}

bool LynxEnv::EnableLevelOrderTraversing() {
  return GetBoolEnv(Key::ENABLE_LEVEL_ORDER_TRAVERSING, false);
}
}  // namespace tasm
}  // namespace lynx
